﻿using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using Dsl.Ped.Controls.ProjectiveControl.Helpers;

namespace Dsl.Ped.Controls.ProjectiveControl
{
    /// <summary>
    /// The main component responsible for behavior of projection. It contains a set of 
    /// <see cref="ProjectionContainerControl"/> instance and enables to switch among them.
    /// </summary>
    public class ProjectiveControl : Selector
    {
        #region Fields

        /// <summary>
        /// Is the projective control activated (selected) 
        /// </summary>
        public static readonly DependencyProperty IsProjectiveControlActivatedProperty;

        /// <summary>
        /// The selected background property.
        /// </summary>
        public static readonly DependencyProperty SelectedBackgroundProperty;

        /// <summary>
        /// View mode (Extended - all projection options are displayed/Normal - no projection options are displayed)
        /// </summary>
        public static readonly DependencyProperty ViewModeProperty;

        /// <summary>
        /// <see cref="RoutedEvent"/> raised when the <see cref="ProjectiveControl"/> gets activated
        /// </summary>
        public static readonly RoutedEvent ActivatedEvent;

        /// <summary>
        /// <see cref="RoutedEvent"/> raised when the <see cref="ProjectiveControl"/> updates its activated status. 
        /// The neerest ancestor of <see cref="ProjectiveControl"/> that handles this event is activated.
        /// </summary>
        private static readonly RoutedEvent UpdateProjectiveControlActivatedEvent;

        private static ProjectiveControl activatedProjectiveControl;

        private static IInputElement lastControl = null;

        private static readonly DependencyPropertyKey IsProjectiveControlActivatedKey;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes static members of the <see cref="ProjectiveControl"/> class. 
        /// </summary>
        static ProjectiveControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ProjectiveControl), new FrameworkPropertyMetadata(typeof(ProjectiveControl)));

            IsProjectiveControlActivatedKey = DependencyProperty.RegisterAttachedReadOnly(
                "IsProjectiveControlActivated",
                typeof(bool),
                typeof(ProjectiveControl),
                new FrameworkPropertyMetadata(
                    false,
                    new PropertyChangedCallback(OnIsProjectiveControlActivatedChanged),
                    new CoerceValueCallback(CalculateIsProjectiveControlActivated)));
            IsProjectiveControlActivatedProperty = IsProjectiveControlActivatedKey.DependencyProperty;

            UpdateProjectiveControlActivatedEvent = EventManager.RegisterRoutedEvent(
                "UpdateProjectiveControlActivated", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProjectiveControl));

            ActivatedEvent = EventManager.RegisterRoutedEvent(
                "Activated", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProjectiveControl));

            SelectedBackgroundProperty = DependencyProperty.Register(
                "SelectedBackground",
                typeof(Brush),
                typeof(ProjectiveControl),
                new FrameworkPropertyMetadata(
                    new SolidColorBrush(Colors.LightBlue), 
                    FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender | FrameworkPropertyMetadataOptions.AffectsRender));

            ViewModeProperty = DependencyProperty.Register(
                "ViewMode",
                typeof(ViewMode),
                typeof(ProjectiveControl),
                new FrameworkPropertyMetadata(
                    ViewMode.Common, FrameworkPropertyMetadataOptions.Inherits));

            EventManager.RegisterClassHandler(typeof(ProjectiveControl), MouseDownEvent, new MouseButtonEventHandler(OnMouseDownEvent), true);
            EventManager.RegisterClassHandler(typeof(ProjectiveControl), UpdateProjectiveControlActivatedEvent, new RoutedEventHandler(OnUpdateProjectiveControlActivatedEvent));
            EventManager.RegisterClassHandler(typeof(ProjectiveControl), ContextMenuOpeningEvent, new RoutedEventHandler(OnContextMenuOpeningEvent), true);
            EventManager.RegisterClassHandler(typeof(ProjectiveControl), SelectionChangedEvent, new RoutedEventHandler(OnSelectionChangedEvent));
        }

        #endregion

        #region Members

        public static bool GetIsProjectiveControlActivated(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsProjectiveControlActivatedProperty);
        }

        /// <summary>
        /// Currently selected background
        /// </summary>
        public Brush SelectedBackground
        {
            get
            {
                return (Brush)base.GetValue(SelectedBackgroundProperty);
            }
            set
            {
                base.SetValue(SelectedBackgroundProperty, value);
            }
        }

        /// <summary>
        /// Mode of the projective control, for instance: common - no additional information are displayed
        /// Extended - additional information are displayed
        /// </summary>
        public ViewMode ViewMode
        {
            get
            {
                return (ViewMode)base.GetValue(ViewModeProperty);
            }
            set
            {
                base.SetValue(ViewModeProperty, value);
            }
        }

        #endregion

        /// <summary>
        /// Calculate whether the projective control is selected or not
        /// </summary>
        /// <param name="item"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static object CalculateIsProjectiveControlActivated(DependencyObject item, object value)
        {
            // This method is called when the IsProjectionContainerSelected property is being calculated
            // for a Projective Control.  
            var isActivated = (item == activatedProjectiveControl);
            var result = isActivated & (BehaviourModeHelper.GetCurrentMode() == BehaviorMode.Activation);

            return result;
        }

        /// <summary>
        /// Handler is executed when the selection is changed
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        private static void OnIsProjectiveControlActivatedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is ProjectiveControl)
            {
                if (e.NewValue != e.OldValue)
                {
                    var routedEventArgs = new RoutedEventArgs {
                        RoutedEvent = ActivatedEvent,
                        Source = obj
                    };

                    (obj as ProjectiveControl).RaiseEvent(routedEventArgs);
                }
            }
        }

        /// <summary>
        /// When the items (<see cref="ProjectionContainerControl"/>) are changed, this method is executed
        /// </summary>
        /// <param name="e"></param>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);

            if (!base.HasItems)
            {
                return;
            }

            // Set the first projection to be displayed
            if (SelectedItem != this.Items[0]) {
                base.SetCurrentValue(Selector.SelectedItemProperty, this.Items[0]);
            }
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new ProjectionContainerControl();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is ProjectionContainerControl);
        }

        protected static void OnUpdateProjectiveControlActivatedEvent(object sender, RoutedEventArgs args)
        {
            // Mark this object as the tree view item over which the mouse
            // is currently positioned.
            activatedProjectiveControl = sender as ProjectiveControl;

            if (activatedProjectiveControl == null) return;

            // Tell that item to re-calculate the IsMouseDirectlyOverItem property
            activatedProjectiveControl.InvalidateProperty(IsProjectiveControlActivatedProperty);

            // Prevent this event from notifying other tree view items higher in the tree.
            args.Handled = true;
        }

        protected static void OnContextMenuOpeningEvent(object sender, RoutedEventArgs args)
        {
            args.Handled = true;

            var projectiveControl = sender as ProjectiveControl;

            // If the projectiveControl that handles this event is not the same like the one which is selected, just
            // pass the Event to be handled somewhere else
            if (activatedProjectiveControl != null && activatedProjectiveControl != projectiveControl)
            {
                args.Handled = false;
                return;
            }

            // Check the projective control is not null
            if (projectiveControl == null)
            {
                return;
            }

            // If the projectiveControl is selected enable the context menu opening
            var isSelected = (bool)projectiveControl.GetValue(IsProjectiveControlActivatedProperty);
            args.Handled = !isSelected;
        }

        private static void OnSelectionChangedEvent(object sender, RoutedEventArgs args) 
        {
            if (sender != activatedProjectiveControl) 
            {
                args.Handled = true;
                return;
            }

            args.Handled = false;
        }

        /// <summary>
        /// MouseDown event occured on some sub-element
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected static void OnMouseDownEvent(object sender, MouseButtonEventArgs args)
        {
            if (BehaviourModeHelper.GetCurrentMode() != BehaviorMode.Activation)
            {
                if (activatedProjectiveControl != null)
                {
                    DependencyObject obj = activatedProjectiveControl;
                    obj.InvalidateProperty(IsProjectiveControlActivatedProperty);

                    activatedProjectiveControl = null;
                }

                return;
            }

            lock (IsProjectiveControlActivatedProperty)
            {
                // Get the element that is currently under the mouse.
                IInputElement currentControl = Mouse.DirectlyOver;

                if (activatedProjectiveControl != null)
                {
                    if (currentControl == lastControl)
                    {
                        return;
                    }
                }

                lastControl = currentControl;

                if (activatedProjectiveControl != null)
                {
                    // Tell the item that previously had the mouse that it no longer does.
                    DependencyObject oldItem = activatedProjectiveControl;
                    activatedProjectiveControl = null;
                    oldItem.InvalidateProperty(IsProjectiveControlActivatedProperty);
                }

                // See if the mouse is still over something (any element, not just a ProjectiveControl item).
                if (currentControl != null)
                {
                    currentControl.RaiseEvent(new RoutedEventArgs(UpdateProjectiveControlActivatedEvent));
                }
            }
        }
    }
}
